﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace CashFlow.Entities
{
	public abstract class RecurrenceBase
	{
		#region Fields and Properties
		protected static List<Tuple<string, string, Func<RecurrenceBase>>> recurrenceTypes =
			new List<Tuple<string, string, Func<RecurrenceBase>>>
			{
				new Tuple<string, string, Func<RecurrenceBase>>("1", "Once", () => new RecurrenceOnce()),
				new Tuple<string, string, Func<RecurrenceBase>>("20", "Daily", () => new RecurrenceOnce()),
				new Tuple<string, string, Func<RecurrenceBase>>("30", "Weekly", () => new RecurrenceOnce()),
				new Tuple<string, string, Func<RecurrenceBase>>("40", "Bi-weekly", () => new RecurrenceOnce()),
				new Tuple<string, string, Func<RecurrenceBase>>("50", "Monthly", () => new RecurrenceOnce()),
				new Tuple<string, string, Func<RecurrenceBase>>("60", "Yearly", () => new RecurrenceOnce())
			};
		protected DateTime firstDate;
		protected DateTime? lastDate;
		protected int repeats;
		protected bool hasLastDate;


		public string Id { get; set; }
		public string Name { get; protected set; }

		public DateTime FirstDate
		{
			get
			{
				return firstDate;
			}
			set
			{
				this.firstDate = value;
				CalcLastDate();
			}
		}

		public DateTime? LastDate
		{
			get
			{
				return lastDate;
			}
			set
			{
				this.lastDate = value;
				this.hasLastDate = this.lastDate.HasValue;
				CalcRepeats();
			}
		}

		public bool HasLastDate
		{
			get
			{
				return this.hasLastDate;
			}
			set
			{
				this.hasLastDate = value;
				if (this.hasLastDate)
				{
					this.lastDate = null;
					this.repeats = -1;
				}
				else
				{
					this.Repeats = 1;
				}
			}
		}

		public int Repeats
		{
			get
			{
				return repeats;
			}
			set
			{
				this.repeats = value;
				this.hasLastDate = this.repeats > 0;
				CalcLastDate();
			}
		}

		public static IEnumerable<Tuple<string, string, Func<RecurrenceBase>>> TypeList
		{
			get
			{
				return recurrenceTypes;
			}
		}
		#endregion

		#region Constructors

		public RecurrenceBase()
		{
			this.firstDate = DateTime.UtcNow;
		}
		#endregion

		protected abstract void CalcLastDate();

		protected abstract void CalcRepeats();

		//TODO: vvs p2 do I need this
		//public abstract DateTime GetNextDate();

		public abstract DateTime? GetNextDate(DateTime date);

		public virtual IEnumerable<DateTime> GetNextDates(DateTime date, int days)
		{
			//TODO: vvs p3 do I use thi one?
			return GetNextDates(DateRange.GetRangeForDays(date, days));
		}

		public abstract IEnumerable<DateTime> GetNextDates(DateRange range);

		public abstract IEnumerable<DateTime> GetDates(DateRange dates);

		public static RecurrenceBase GetDefault()
		{
			var recurrence = new RecurrenceOnce();
			return recurrence;
		}

		public static RecurrenceBase GetNew(string id)
		{
			RecurrenceBase result = null;
			var recurrence = recurrenceTypes.FirstOrDefault(rt => rt.Item1 == id);
			if (recurrence == null)
			{
				result = GetDefault();
			}
			else
			{
				result = recurrence.Item3();
			}
			return result;
		}
	}
}